#!/usr/bin/python
#
# XBMC Media Center
# Copyright (C) 2010 Yellow_Panther
# yanick.saugy@gmail.com - http://www.help-info.ch
#
# Based on J2ME Remote (Copyright (c) 2008 topfs2)
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#

import sys
try:
    from xbmcclient import *
    from bt import *
except:
    sys.path.append('../../lib/python')
    from xbmcclient import *
    from bt import *

from socket import *
import os
from threading import Thread

host = "localhost"
port = 9777




######################################################################
# XBMC Client Class
######################################################################

class XBMC_Client:
    """An XBMC event client"""

    def __init__(self, name ="", icon_file=None, broadcast=False, uid=UNIQUE_IDENTIFICATION,
                 ip="127.0.0.1"):
        """
        Keyword arguments:
        name -- Name of the client
        icon_file -- location of an icon file, if any (png, jpg or gif)
        uid  -- unique identification
        """
        self.name = str(name)
        self.icon_file = icon_file
        self.icon_type = self._get_icon_type(icon_file)
        self.ip = ip
        self.port = 9777
        self.sock = socket(AF_INET,SOCK_DGRAM)
        if broadcast:
            self.sock.setsockopt(SOL_SOCKET, SO_BROADCAST, 1)
        self.uid = uid


    def connect(self, ip=None, port=None):
        """Initialize connection to XBMC
        ip -- IP Address of XBMC
        port -- port that the event server on XBMC is listening on
        """
        if ip:
            self.ip = ip
        if port:
            self.port = int(port)
        self.addr = (self.ip, self.port)
        packet = PacketHELO(self.name, self.icon_type, self.icon_file)
        return packet.send(self.sock, self.addr, self.uid)


    def close(self):
        """Close the current connection"""
        packet = PacketBYE()
        return packet.send(self.sock, self.addr, self.uid)


    def ping(self):
        """Send a PING packet"""
        packet = PacketPING()
        return packet.send(self.sock, self.addr, self.uid)


    def send_notification(self, title="", message="", icon_file=None):
        """Send a notification to the connected XBMC
        Keyword Arguments:
        title -- The title/heading for the notifcation
        message -- The message to be displayed
        icon_file -- location of an icon file, if any (png, jpg, gif)
        """
        self.connect()
        packet = PacketNOTIFICATION(title, message,
                                    self._get_icon_type(icon_file),
                                    icon_file)
        return packet.send(self.sock, self.addr, self.uid)


    def send_keyboard_button(self, button=None):
        """Send a keyboard event to XBMC
        Keyword Arguments:
        button -- name of the keyboard button to send (same as in Keymap.xml)
        """
        if not button:
            return
        return self.send_button(map="KB", button=button)


    def send_remote_button(self, button=None):
        """Send a remote control event to XBMC
        Keyword Arguments:
        button -- name of the remote control button to send (same as in Keymap.xml)
        """
        if not button:
            return
        return self.send_button(map="R1", button=button)


    def release_button(self):
        """Release all buttons"""
        packet = PacketBUTTON(code=0x01, down=0)
        return packet.send(self.sock, self.addr, self.uid)


    def send_button(self, map="", button="", amount=0):
        """Send a button event to XBMC
        Keyword arguments:
        map -- a combination of map_name and button_name refers to a
               mapping in the user's Keymap.xml or Lircmap.xml.
               map_name can be one of the following:
                   "KB" => standard keyboard map ( <keyboard> section )
                   "XG" => xbox gamepad map ( <gamepad> section )
                   "R1" => xbox remote map ( <remote> section )
                   "R2" => xbox universal remote map ( <universalremote>
                           section )
                   "LI:devicename" => LIRC remote map where 'devicename' is the
                                      actual device's name
        button -- a button name defined in the map specified in map, above.
                  For example, if map is "KB" refering to the <keyboard>
                  section in Keymap.xml then, valid buttons include
                  "printscreen", "minus", "x", etc.
        """
        packet = PacketBUTTON(map_name=str(map), button_name=str(button), amount=amount, repeat=0)
        return packet.send(self.sock, self.addr, self.uid)

    def send_button_state(self, map="", button="", amount=0, down=0, axis=0):
        """Send a button event to XBMC
        Keyword arguments:
        map -- a combination of map_name and button_name refers to a
               mapping in the user's Keymap.xml or Lircmap.xml.
               map_name can be one of the following:
                   "KB" => standard keyboard map ( <keyboard> section )
                   "XG" => xbox gamepad map ( <gamepad> section )
                   "R1" => xbox remote map ( <remote> section )
                   "R2" => xbox universal remote map ( <universalremote>
                           section )
                   "LI:devicename" => LIRC remote map where 'devicename' is the
                                      actual device's name
        button -- a button name defined in the map specified in map, above.
                  For example, if map is "KB" refering to the <keyboard>
                  section in Keymap.xml then, valid buttons include
                  "printscreen", "minus", "x", etc.
        """
        if axis:
          if amount == 0:
            down = 0
          else:
            down = 1

        packet = PacketBUTTON(map_name=str(map), button_name=str(button), amount=amount, down=down, queue=1, axis=axis)
        return packet.send(self.sock, self.addr, self.uid)

    def send_mouse_position(self, x=0, y=0):
        """Send a mouse event to XBMC
        Keywords Arguments:
        x -- absolute x position of mouse ranging from 0 to 65535
             which maps to the entire screen width
        y -- same a 'x' but relates to the screen height
        """
        packet = PacketMOUSE(int(x), int(y))
        return packet.send(self.sock, self.addr, self.uid)

    def send_log(self, loglevel=0, logmessage="", autoprint=True):
        """
        Keyword arguments:
        loglevel -- the loglevel, follows XBMC standard.
        logmessage -- the message to log
        autoprint -- if the logmessage should automaticly be printed to stdout
        """
        packet = PacketLOG(loglevel, logmessage)
        return packet.send(self.sock, self.addr, self.uid)

    def send_action(self, actionmessage="", actiontype=ACTION_EXECBUILTIN):
        """
        Keyword arguments:
        actionmessage -- the ActionString
        actiontype -- The ActionType the ActionString should be sent to.
        """
        packet = PacketACTION(actionmessage, actiontype)
        return packet.send(self.sock, self.addr, self.uid)

    def _get_icon_type(self, icon_file):
        if icon_file:
            if icon_file.lower()[-3:] == "png":
                return ICON_PNG
            elif icon_file.lower()[-3:] == "gif":
                return ICON_GIF
            elif icon_file.lower()[-3:] == "jpg":
                return ICON_JPG
        return ICON_NONE

client = XBMC_Client(name="Bluetooth server")
class Ping(Thread):
    def __init__ (self):
        Thread.__init__(self)
    def run(self):
        import time
        while 1: # ping the server every 19s
            client.ping()
            time.sleep (19)

def main():
    import time

    # connect to localhost, port 9777 using a UDP socket
    # this only needs to be done once.
    # by default this is where XBMC will be listening for incoming
    # connections.
    server_sock=bt_create_rfcomm_socket()
    server_sock.listen(1)

    portBT = server_sock.getsockname()[1]

    uuid = "ACDC"
    bt_advertise(socket=server_sock, name="XBMC Remote", uuid=uuid)

    print "Waiting for connection on RFCOMM channel %d" % portBT

    client.connect(host,port)

    Ping().start()

    while(True):
        try:
            time.sleep(2)
            try:
                client_sock, client_info = server_sock.accept()
                print "Accepted connection from ", client_info
	    except KeyboardInterrupt:
        	client.send_log(LOGNOTICE, "Bluetooth server interrupted")
                bt_stop_advertising(socket = server_sock)
                server_sock.close()
                sys.exit(0)
            except:
                pass
            client_addr, client_port  = client_info
            runFlag  = True
            firstRun = True
        except KeyboardInterrupt:
            client.send_log(LOGNOTICE, "J2ME: User interrupted")
            bt_stop_advertising(socket = server_sock)
            server_sock.close()
            sys.exit(0)
        try:
            while runFlag:
                if firstRun:
                    client_name = client_sock.recv(1024)
                    firstRun = False
                    client.send_notification(client_name, "Phone connected")
                    client.send_log(LOGNOTICE, "Phone Connected")
                data = client_sock.recv(4)
                if   ord(data[0]) == 0xA0:
                    client.send_keyboard_button("up")
                elif ord(data[0]) == 0xA1:
                    client.send_keyboard_button("down")
                elif ord(data[0]) == 0xA2:
                    client.send_keyboard_button("left")
                elif ord(data[0]) == 0xA3:
                    client.send_keyboard_button("right")
                elif ord(data[0]) == 0xA4:
                    client.send_keyboard_button("volume_mute")
                elif ord(data[0]) == 0xA5:
                    client.send_keyboard_button("enter")
                elif ord(data[0]) == 0xA6:
                    client.send_keyboard_button("volume_down")
                elif ord(data[0]) == 0xA7:
                    client.send_keyboard_button("volume_up")
                else:
                    client.send_keyboard_button(data[0])
#                print ord(data[0])

        except IOError:
            print "Lost connection to %s" % client_name
            runFlag = False
            client.send_notification(client_name, "Phone disconnected")
            pass
        except KeyboardInterrupt:
            client.send_log(LOGNOTICE, "J2ME: User interrupted")
            client_sock.close()
            bt_stop_advertising(socket = server_sock)
            server_sock.close()
            time.sleep(2)
            client.close()
            sys.exit(0)
        except:
            pass

def usage():
    print """
Android Bluetooth Phone Remote Control Client for XBMC

Usage for server.py
    --address ADDRESS  (default: localhost)
    --port PORT        (default: 9777)
    --help             (Brings up this message)
"""

if __name__=="__main__":
    try:
        i = 0
        while (i < len(sys.argv)):
            if   (sys.argv[i] == "--address"):
                host = sys.argv[i + 1]
            elif (sys.argv[i] == "--port"):
                port = sys.argv[i + 1]
            elif (sys.argv[i] == "--help"):
                usage()
                sys.exit(0)
            i = i + 1
        main()
    except Exception as e:
        print "Error: ", e
        sys.exit(0)

